import datetime
from google.appengine.ext import db
import main

class Project(db.Model):

    """Model for a Cheeseshop project.

    Entities are keyed on the project's Cheeseshop name.

    """

    ### Project metadata
    name = db.StringProperty(required=True)
    # XXX auto-set from name somehow?
    lowercase_name = db.StringProperty(required=True)
    latest_release = db.StringProperty(default='0.0')
    # Does the project support Python 3?  None represents an unknown state.
    py3k_support = db.BooleanProperty(default=None)
    # Short explanation as to why the project supports Python 3;
    # for when the trove classifier is not set for Python 3 (or 2).
    # When the classifier is found, set back to None.
    py3k_note = db.StringProperty(multiline=False)
    # For changelog feed
    py3k_added = db.DateProperty()

    ### Download metrics
    # Latest release details
    latest_downloads = db.IntegerProperty(default=0)
    download_rate = db.FloatProperty(default=0.0)
    # Largest release details
    largest_release = db.StringProperty(default='0.0')
    largest_downloads = db.IntegerProperty(default=0)
    # www.python.org poll results
    poll_count = db.IntegerProperty(default=0)


    @classmethod
    def create(cls, name):
        """Create and return an (uncommitted!) entity."""
        return cls(key_name=name, name=name, lowercase_name=name.lower())

    def py3k_support_added(self, note=None):
        """Mark the project as supporting Python 3."""
        if not (self.py3k_support and self.py3k_note is None):
            self.py3k_note = note
        if not self.py3k_support:
            # Only update the date when support is actually new; prevent
            # unneeded notification to others of support.
            self.py3k_support = True
            self.py3k_added = datetime.date.today()

    def py3k_classifier(self, classifiers):
        """Set py3k support if the classifiers specify Python 3 is supported
        (or explicitly is not)."""
        found = None
        for classifier in classifiers:
            bits = classifier.split('::')
            bits = [bit.strip() for bit in bits]
            # E.g., Programming Language :: Python :: 3
            if len(bits) != 3:
                continue
            if bits[0] != 'Programming Language':
                continue
            elif bits[1] != 'Python':
                continue
            elif bits[2].startswith('3'):
                self.py3k_support_added()
                break
            elif bits[2].startswith('2'):
                found = False
                # Don't break as a Python 3 classifier might still be out there
        else:
            # Clearly does not support Python 3 according to the project, but
            # only set support to False if not already flagged as supported
            # since a fork might be covering the project.
            if found is False:
                if ((self.py3k_support is None and not self.py3k_note) or
                        self.py3k_support is False):
                    self.py3k_support = False
                    self.py3k_note = None
                    self.py3k_added = None

    def is_stale(self, version):
        """See if the entity is stale compared to the specified version."""
        return (main.normalized_version(version) >=
                main.normalized_version(self.latest_release))

    def data_update(self, version, data):
        """Update the entity with release data from the Cheeseshop."""
        if not self.is_stale(version):
            return
        self.latest_release = version
        self.py3k_classifier(data.get('classifiers', []))

    def urls_update(self, version, data):
        """Update the entity with release URLs data from the Cheeseshop."""
        download_total = sum(file_.get('downloads', 0) for file_ in data)
        if version == self.latest_release:
            today = datetime.date.today()
            self.latest_downloads = download_total
            self.download_rate = 0.0
            for file_detail in data:
                try:
                    when = datetime.date.fromordinal(file_detail['upload_time'])
                except KeyError:
                    continue
                download_count = file_detail.get('downloads', 0)
                if download_count == 0:
                    continue
                days_past = ((today - when).days * 1.0) or 1.0
                self.download_rate += download_count / days_past
        if download_total >= self.largest_downloads:
            self.largest_downloads = download_total
            self.largest_release = version


class ProjectDetails(db.Model):

    """Project-wide, persistent details."""

    latest_pypi_changes = db.DateTimeProperty(
                                        default=datetime.datetime(1970, 1, 1))
    new_projects_pickle = db.BlobProperty()
    latest_changes_pickle = db.BlobProperty()
    latest_urls_pickle = db.BlobProperty()

    @classmethod
    def singleton(cls):
        """Return the singleton for the class.

        Assumes that the singleton has already been created.

        """
        key_name = '/__project_details'
        singleton = cls.get_by_key_name(key_name)
        if singleton is None:
            singleton = cls(key_name=key_name)
        return singleton
